﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="EntityFrameworkHelpers.cs" company="DHGMS Solutions">
//   Licensed under GNU General Public License version 2 (GPLv2)
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
namespace Dhgms.Nucleotide.Generators
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    using Dhgms.Nucleotide.Model;
    using Dhgms.Nucleotide.PropertyInfo;

    /// <summary>
    ///     Helper for generating the Entity Framework helpers
    /// </summary>
    public class EntityFrameworkHelpers
    {
        #region Public Methods and Operators

        /// <summary>
        /// Entry point for generating the code
        /// </summary>
        /// <param name="mainNamespaceName">
        /// The main namespace
        /// </param>
        /// <param name="subNamespace">
        /// The sub namespace, if any
        /// </param>
        /// <param name="classes">
        /// Collection of classes to generate a helper for
        /// </param>
        /// <returns>
        /// C# code
        /// </returns>
        public string Generate(string mainNamespaceName, string subNamespace, List<Tuple<IClassGenerationParameters, string>> classes)
        {
            if (string.IsNullOrWhiteSpace(mainNamespaceName))
            {
                throw new ArgumentNullException("mainNamespaceName");
            }

            var sb = new StringBuilder();

            var noSubNamespace = from c in classes
                                where string.IsNullOrWhiteSpace(c.Item1.SubNamespace)
                                select c;

            this.DoSubNamespace(sb, mainNamespaceName, null, noSubNamespace);

            var subNamespaces = from c in classes
                                where !string.IsNullOrWhiteSpace(c.Item1.SubNamespace)
                                group c by c.Item1.SubNamespace
                                    into g
                                    select g;

            foreach (var ns in subNamespaces)
            {
                this.DoSubNamespace(sb, mainNamespaceName, ns.Key, ns);
            }

            return sb.ToString();
        }

        private void DoSubNamespace(StringBuilder sb, string mainNamespaceName, string subNamespace, IEnumerable<Tuple<IClassGenerationParameters, string>> classes)
        {
            var fullyQualifiedNamespace = mainNamespaceName + ".Model.Helper.EntityFramework"
                                             + (!string.IsNullOrWhiteSpace(subNamespace)
                                                    ? "." + subNamespace
                                                    : null);

            sb.AppendLine("namespace " + fullyQualifiedNamespace);
            sb.AppendLine("{");
            sb.AppendLine("    using System;");
            sb.AppendLine("    using System.Data.Entity;");
            sb.AppendLine(string.Empty);
            sb.AppendLine(Helpers.GetAutoGeneratedWarning());

            this.DoOurMethodsRegion(sb, classes);

            sb.AppendLine("}");
        }

        #endregion

        #region Methods

        /// <summary>
        /// Generates the code that maps the class to a database table t
        /// </summary>
        /// <param name="sb">
        ///     The string builder to add the code to
        /// </param>
        /// <param name="classInfo">
        ///     The information relating to the generation of the class
        /// </param>
        /// <param name="inheritingFullyQualifiedClass">
        /// The fully qualified class name for the EF inheriting class
        /// </param>
        private static void DoEntityFrameworkModelMethod(StringBuilder sb, IClassGenerationParameters classInfo, string inheritingFullyQualifiedClass)
        {
            if (classInfo == null)
            {
                throw new ArgumentNullException("classInfo");
            }

            if (string.IsNullOrWhiteSpace(classInfo.ClassName))
            {
                throw new ArgumentException("ClassName");
            }

            if (classInfo.Properties == null)
            {
                throw new ArgumentException("Properties");
            }

            if (classInfo.Properties.Count(p => p.IsKey) > 1)
            {
                throw new ArgumentException("Too many primary keys defined");
            }

            // currently not going to support list types in our EF mapping
            if ((classInfo.BaseClassProperties != null
                 && classInfo.BaseClassProperties.Any(baseProperty => baseProperty.Collection != CollectionType.None))
                || classInfo.Properties.Any(property => property.Collection != CollectionType.None))
            {
                return;
            }

            string fullyQualifiedClassName = classInfo.MainNamespaceName + ".Model.Info."
                                             + (!string.IsNullOrWhiteSpace(classInfo.SubNamespace)
                                                    ? classInfo.SubNamespace + "."
                                                    : null) + classInfo.ClassName;

            sb.AppendLine("/// <summary>");
            sb.AppendLine("/// Maps the information class to the entity framework model");
            sb.AppendLine("/// </summary>");
            sb.AppendLine("/// <param name=\"modelBuilder\">");
            sb.AppendLine("/// model builder object");
            sb.AppendLine("/// </param>");
            sb.AppendLine("/// <param name=\"schemaName\">");
            sb.AppendLine("/// The schema Name in the database");
            sb.AppendLine("/// </param>");
            sb.AppendLine("/// <param name=\"tableName\">");
            sb.AppendLine("/// The table Name in the database");
            sb.AppendLine("/// </param>");
            sb.AppendLine(
                "public void DoEntityFrameworkModel(DbModelBuilder modelBuilder, string schemaName, string tableName)");
            sb.AppendLine("{");
            sb.AppendLine("    if (modelBuilder == null)");
            sb.AppendLine("    {");
            sb.AppendLine("        throw new ArgumentNullException(\"modelBuilder\");");
            sb.AppendLine("    }");
            sb.AppendLine(string.Empty);
            sb.AppendLine("    if (tableName == null)");
            sb.AppendLine("    {");
            sb.AppendLine("        throw new ArgumentNullException(\"tableName\");");
            sb.AppendLine("    }");
            sb.AppendLine(string.Empty);

            foreach (PropertyInfoBase p in classInfo.Properties.Where(p => p.IsKey))
            {
                sb.AppendLine(
                    "    modelBuilder.Entity<" + fullyQualifiedClassName + ">().HasKey(x => x." + p.Name + ");");
            }

            foreach (PropertyInfoBase p in classInfo.Properties)
            {
                sb.Append(
                    "    modelBuilder.Entity<" + fullyQualifiedClassName + ">().Property(x => x." + p.Name + ").Is");
                sb.AppendLine(p.Optional ? "Optional();" : "Required();");
            }

            foreach (
                PropertyInfoBase p in classInfo.Properties.Where(p => !string.IsNullOrWhiteSpace(p.AlternativeDatabaseColumnName)))
            {
                sb.AppendLine(
                    "    modelBuilder.Entity<" + fullyQualifiedClassName + ">().Property(x => x." + p.Name
                    + ").HasColumnName(\"" + p.AlternativeDatabaseColumnName + "\");");
            }

            if (string.IsNullOrWhiteSpace(inheritingFullyQualifiedClass))
            {
                sb.AppendLine("    modelBuilder.Entity<" + fullyQualifiedClassName + ">().ToTable(tableName, schemaName);");
            }
            else
            {
                sb.AppendLine("    modelBuilder.Entity<" + inheritingFullyQualifiedClass + ">().Map(m =>");
                sb.AppendLine("    {");
                sb.AppendLine("        m.MapInheritedProperties();");
                sb.AppendLine("        m.ToTable(tableName, schemaName);");
                sb.AppendLine("    });");
            }

            sb.AppendLine("}");
            sb.AppendLine(string.Empty);
        }

        /// <summary>
        /// Generates the code for the our methods region
        /// </summary>
        /// <param name="sb">
        /// StringBuilder to add the code to
        /// </param>
        /// <param name="classes">
        /// Collection of classes to generate helpers for
        /// </param>
        private void DoOurMethodsRegion(StringBuilder sb, IEnumerable<Tuple<IClassGenerationParameters, string>> classes)
        {
            foreach (var classInfo in classes)
            {
                var fullyQualifiedClassName = classInfo.Item1.MainNamespaceName + ".Model.Info." + (!string.IsNullOrWhiteSpace(classInfo.Item1.SubNamespace) ? classInfo.Item1.SubNamespace + "." : null) + classInfo.Item1.ClassName;

                sb.AppendLine("    /// <summary>");
                sb.AppendLine("    /// Helper methods for using POCO and Entity Framework");
                sb.AppendLine("    /// </summary>");
                sb.AppendLine("    public class " + classInfo.Item1.ClassName);
                sb.AppendLine("                : Dhgms.DataManager.Model.IEntityFramework<" + fullyQualifiedClassName + ">");
                sb.AppendLine("    {");

                DoEntityFrameworkModelMethod(sb, classInfo.Item1, classInfo.Item2);

                sb.AppendLine("    }");
            }
        }

        #endregion
    }
}